package org.NooLab.docserver.main;

import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import org.NooLab.docobserver.structures.DocumentObserverIntf;
import org.NooLab.docserver.component.WatchedFile;
import org.NooLab.docserver.properties.DocoControlProperties;
import org.NooLab.docserver.properties.DocumentObserverProperties;
import org.NooLab.docserver.storage.DocoDataBase;
import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.net.GUID;


/**
 * 
 * This abstract class serves as a basis for the classes that observe either files or folders
 * 
 * It contains an inner class that periodically checks an explicit list of files (folders) for
 * changes. If a change has been detected, an java Observer event (notification) will be released.
 * 
 * This event will be digested by the main class "DocumentObserver" (where the update() method of 
 * the Java Observer is located), which then routes the message to "ObserverMsgHandling" 
 *
 */
public abstract class DocumentObserverCoreAbstract extends Observable{

	protected DocumentObserver parent;
	protected DocumentObserverProperties docoProperties;
	protected DocoDataBase docoDataBase;
	protected DocumentObserverFactory docoFactory;
	protected DocoControlProperties docoControlProperties ;
	
	protected String guid = "" ;
	protected int index = -1;
	
	Thread docoThrd;
	int observerPeriod = 5000;
	
	
	
	protected ArrayList<WatchedFile> watchedFiles = new ArrayList<WatchedFile>();
	protected boolean observesDirectories = false;

	protected ArrayList<WatchedFile> changedWFs = new ArrayList<WatchedFile>();
	protected List<String> patterns = new ArrayList<String>();
	
	protected long folderId = -3; // id of the host folder
	
	boolean changed = false;
	ObserverProcess observerProc; 
	protected int delay = 100 ;
		
	protected PrintLog out = new PrintLog(2,false);
	protected int startup=0;
		
	// ========================================================================
	public DocumentObserverCoreAbstract( DocumentObserver parent, int ix) throws Exception{
		
		if ((parent==null) || (ix<0)){
			throw(new Exception("unrooted instantiation of doco-cores is not allowed"));
		}

		guid = GUID.uniquevalue();
		this.parent = parent ;
		index = ix;

		docoProperties = parent.docoProperties;
		docoDataBase = parent.docoDataBase ;
		docoFactory = parent.docoFactory ;
		
		addObserver((Observer) parent) ;
		
	}
	// ========================================================================	
	
	protected abstract void loadWatches() throws FileNotFoundException ;
	

	public void stop(){
		
		
		observerProc.isRunning = false;
		int z=0;
		while ((startup>=0) && (z<100)){
			out.delay(10) ; z++;	
		}
		
	}
	
	// ------------------------------------------------------------------------
	class ObserverProcess implements Runnable{

		boolean isActive;
		boolean isRunning = false;
		public String thrdName;
		
		ArrayList<WatchedFile> listOfFileWatches = new ArrayList<WatchedFile>();
		
		// ................................................
		public ObserverProcess(){
			thrdName = "docoThrd-"+index ;
			docoThrd = new Thread(this, thrdName);
			
		}
		public void start() {
			// TODO Auto-generated method stub
			docoThrd.start() ;
		}
		// ................................................
		
		@Override
		public void run() {
			
			isRunning = true;
			int _active=0;
	    	String ds="";
	    	
	    	startup=0 ;
			isActive=true ;
			changed =false;
			
			try {
				while ((isActive==true) && (isRunning)){
			    	  out.delay(delay);
			    	  // System.out.println("In MyThread, count is " + count);
			    	   
					  
					  if (_active==0){
						  
						  _active=1;
					  
						  changedWFs.clear();
						  
						  if (isActive==true){
							  // listOfFileWatches = prepareListOfWatchedFiles() ;
							  loadWatches() ;
						  }
			        
						  if ((changedWFs.size()>0) ){
					        	
				    		  try{
				    			  // protecting from programmatic series of file actions
				    			  out.delay(30);
				    		  }catch(Exception e) {}
				    		  
				    		  // watchedFiles
				    		  int w = 0;
				    		  // for ( int w=0; w<watchedFiles.size();w++){ 
				    		  while (  changedWFs.size()>0){
				    			  
				    			  if ((changedWFs.size()==0) || (w>changedWFs.size())){
				    				  break;
				    			  }
				    			  WatchedFile wF = changedWFs.get(w) ;
				    	    	  if (wF==null){ 
				    	    		  changedWFs.remove(0);
				    	    		  continue;
				    	    	  }
				    	    	  
				    	    	  wF.setFolderId(folderId);
				    	    	  
				    			  if (wF.getRemoveThis() == -3){
				    	    		// remove this entry, it was deleted
				    	    		// notify about deleted, flag is "DEL: "

			    					  String tmp = wF.getObservation() ;
			    					  
			    					  String _msg = ( wF.getObservation() +": "+ wF.getFilename());

			    					  
			    					  // clone it
			    					  
			    					  WatchedFile rwf = new WatchedFile( wF );
			    					  
			    					  setChanged();
			    					  notifyObservers( rwf ); // (Object)
			    					  
			    					  int k = wF.findItem( watchedFiles );
			    					  if (k>=0){
			    						  watchedFiles.remove(k);
			    					  }
			    					  changedWFs.remove(w) ;
				    				  if (w>=1){
				    					  w=w-1;
				    				  }
				    			  }
				    			  else{
				    				  if ( (wF.isChanged()==true) && (wF.getRemoveThis() >= -1)){
					    	    		
				    					  setChanged();
				    					  notifyObservers( wF ); // (Object)
				    	    				
				    					  // System.out.println("recognized change : "+wF.filename);
				    					  
				    					  wF.setChanged(false);
				    					  if ( wF.getRemoveThis()>-3){
				    						  wF.setRemoveThis(-1);
				    					  }
				    					  changedWFs.set(w,wF) ;
				    					  changedWFs.remove(0);
				    				  } // wF changed ?

				    				  
				    			  }
				    			  
				    		  } // loop ( for, while)
				    		  
				    	  } // changedWF ?
						  
						  _active=0;
					  } // _active==0 ?
					  
					  int z=0;
					  while ((z<observerPeriod/10) && (isRunning)){
						  parent.out.delay(10) ; 
						  z++;
					  }
			    } // while -> 
			        
				
				// System.out.println("Listener: File or Directory not found, thread interrupted.\n"+e.getStackTrace()+"\n");
			} catch (Exception exc) {
			    
				System.out.println("Listener thread <"+thrdName+"> interrupted.");
				exc.printStackTrace() ;
			}
			startup=-1 ;
			out.print(3,"Listener thread  <docoThrd-"+index+"> has been stopped.");

		}
		
	}
	// ------------------------------------------------------------------------

	
	protected void resetallFlags(){
		WatchedFile wF ;
		for ( WatchedFile F: watchedFiles){
			// does this work  ????
			F.setChanged(false) ;
			F.setRemoveThis(0) ;
			F.setObservation("");
			F.setInternalFlag( DocumentObserverIntf._FILE_CHANGE_NIL );
		}
	}


	protected int isDifferent( WatchedFile wF ){
		int rb=-1;
		int x=-1;
		
		for ( WatchedFile F:watchedFiles){
			if ( (F.getFilename().contentEquals(wF.getFilename())) && ( F.isChanged() == false )){
				
				if ( (F.getLDate() != wF.getLDate()) ||
				     (F.getFilesize()) != wF.getFilesize()){
					rb=1;
					x=1;
					break ;
				}
			}
		}
		if (x<0){ rb=0;};
		
		return rb;
		
	}
	
	protected int isKnownbyName(WatchedFile wF){
		int rval=-1,i=-1;
		
		for ( WatchedFile F:watchedFiles){
			i++;
			
			if (( (F.getFilename().contentEquals( wF.getFilename()))) && (F.getInternalFlag() >= -1))	{
				rval=i;
				break ;
			}
			
		}
		
		return rval;
	}

	protected boolean isUnknown(WatchedFile wF){
		boolean rb=true;
		
		for ( WatchedFile F:watchedFiles){
			if ( (F.getFilename() == wF.getFilename()) &&
				 (F.getLDate() == wF.getLDate()) &&
				 (F.getFilesize()) == wF.getFilesize())	{
				rb=false;
				break ;
			}
		}
		
		return rb;
	}
	
	// ----------------------------------------------------
	

	public List<String> getPatterns() {
		return patterns;
	}


	public void setPatterns(List<String> patterns) {
		this.patterns = patterns;
	}


	public String getGuid() {
		return guid;
	}


	public int getIndex() {
		return index;
	}



	public ArrayList<WatchedFile> getWatchedFiles() {
		return watchedFiles;
	}


	
	
}
